# plugs/todo.py
#
#

""" provide todo related commands """

__copyright__ = 'this file is in the public domain'

from gozerbot.generic import strtotime, striptime, getwho, today, lockdec
from gozerbot.commands import cmnds
from gozerbot.examples import examples
from gozerbot.users import users
from gozerbot.datadir import datadir
from gozerbot.persist import Persist
from gozerbot.plughelp import plughelp
from gozerbot.aliases import aliases
import time, thread

plughelp.add('todo', 'manage todo lists .. by user or by channel')

todolock = thread.allocate_lock()

class Todoitem:

    """ a todo item """

    def __init__(self, name, descr, ttime=None, duration=None, warnsec=None, \
priority=None):
        self.name = name
        self.time = ttime
        self.duration = duration
        self.warnsec = warnsec
        self.descr = descr
        self.priority = priority
        self.num = 0

    def __str__(self):
        return "name: %s num: %d time: %s duration: %s warnsec: %s \
description: %s priority: %s" % (self.name, self.num, \
time.ctime(self.time), self.duration, self.warnsec, self.descr, self.priority)

class Todolist:

    """ a dict faking list of todo items .. index is number """

    def __init__(self):
        self.max = 0
        self.data = {}

    def __len__(self):
        return len(self.data)

    def __getitem__(self, num):
        return self.data[num]

    def __delitem__(self, num):
        del self.data[num]

    def __iter__(self):
        tmplist = self.data.values()
        tmplist.sort(lambda x, y: cmp(x.priority, y.priority), reverse=True)
        return tmplist.__iter__()

    def append(self, item):
        """ add todo item """
        self.max += 1
        item.num = self.max
        self.data[self.max] = item

    def __str__(self):
        return str(self.data)

locked = lockdec(todolock)

class Todo(Persist):

    """ Todoos """

    @locked
    def __init__(self, filename):
        Persist.__init__(self, filename)
        if not self.data:
            return
        for key in self.data.keys():
            todoos = self.data[key]
            for (k, v) in todoos.data.items():
                v.num = k
            newd = Todolist()
            for i in todoos:
                newd.append(i)
            self.data[key] = newd

    def size(self):
        return len(self.data)

    def get(self, name):
        """ get todoos of <name> """
        if self.data.has_key(name):
            return self.data[name]

    @locked
    def add(self, name, txt, ttime):
        """ add a todo """
        name = name.lower()
        if not self.data.has_key(name):
            self.data[name] = Todolist()
        self.data[name].append(Todoitem(name, txt.strip(), ttime))
        self.save()
        return 1

    @locked
    def addnosave(self, name, txt, ttime):
        """ add but don't save """
        name = name.lower()
        if not self.data.has_key(name):
            self.data[name] = Todolist()
        self.data[name].append(Todoitem(name, txt, ttime))

    @locked
    def delete(self, name, nrlist):
        """ delete todo item """
        if not self.data.has_key(name):
            return 0
        nrlist.sort()
        nrlist.reverse()
        todoos = self.data[name]
        teller = 0
        for i in nrlist:
            try:
                del todoos[i]
                teller += 1
            except KeyError:
                pass
        self.save()
        return teller

    def toolate(self, name):
        """ show if there are any time related todoos that are too late """
        now = time.time()
        teller = 0
        for i in self.data[name]:
            if i.time < now:
                teller += 1
        return teller

    def timetodo(self, name):
        """ show todoos with time field set """
        result = []
        if not self.data.has_key(name):
            return result
        for i in self.data[name]:
            if i.time:
                result.append(i)
        return result

    def withintime(self, name, time1, time2):
        """ show todoos within time frame """
        result = []
        if not self.data.has_key(name):
            return result
        for i in self.data[name]:
            if i.time >= time1 and i.time < time2:
                result.append(i)
        return result

todo = Todo(datadir + '/todo')

if not todo.data:
    todo.data = {}

def handle_todo(bot, ievent):
    """ todo [<item>] .. show todoos or set todo item """
    if len(ievent.args) > 0:
        handle_todo2(bot, ievent)
        return
    user = users.getuser(ievent.userhost)
    try:
        todoos = todo.get(user.name)
    except KeyError:
        ievent.reply('i dont have todo info for %s' % user.name)
        return
    saytodo(bot, ievent, todoos)

def handle_todo2(bot, ievent):
    """ set todo item """
    if not ievent.rest:
        ievent.missing("<what>")
        return
    else:
        what = ievent.rest
    user = users.getuser(ievent.userhost)
    ttime = strtotime(what)
    if not ttime  == None:
        ievent.reply('time detected ' + time.ctime(ttime))
        what = striptime(what)
        todo.add(user.name, what, ttime)
    else:
        todo.add(user.name, what, None)
    ievent.reply('todo item added')

cmnds.add('todo', handle_todo, 'USER')
examples.add('todo', 'todo [<item>] .. show todo items or add a todo item', \
'1) todo 2) todo program the bot 3) todo 22:00 sleep')

def handle_tododone(bot, ievent):
    """ todo-done <listofnrs> .. remove todo items """
    if len(ievent.args) == 0:
        ievent.missing('<list of nrs>')
        return
    try:
        nrs = []
        for i in ievent.args:
            nrs.append(int(i))
    except ValueError:
        ievent.reply('%s is not an integer' % i)
        return
    user = users.getuser(ievent.userhost)
    nrdone = todo.delete(user.name, nrs)
    if nrdone == 1:
        ievent.reply('%s item deleted' % nrdone)
    elif nrdone == 0:
        ievent.reply('no items deleted')
    else:
        ievent.reply('%s items deleted' % nrdone)

cmnds.add('todo-done', handle_tododone, 'USER')
examples.add('todo-done', 'todo-done <listofnrs> .. remove items from \
todo list', '1) todo-done 1 2) todo-done 3 5 8')
aliases.data['done'] = 'todo-done'

def handle_chantodo(bot, ievent):
    """ todo-chan [<item>] .. show todoos or set todo item for channel"""
    if ievent.rest:
        handle_chantodo2(bot, ievent)
        return
    todoos = todo.get(ievent.channel)
    if not todoos:
        ievent.reply('i dont have todo info for %s' % \
ievent.channel)
        return
    saytodo(bot, ievent, todoos)

def handle_chantodo2(bot, ievent):
    """ set todo item for channel"""
    what = ievent.rest
    ttime = strtotime(what)
    if not ttime  == None:
        ievent.reply('time detected ' + time.ctime(ttime))
        result = '(%s) ' % ievent.nick + striptime(what)
        todo.add(ievent.channel, result, ttime)
    else:
        result = '(%s) ' % ievent.nick + what
        todo.add(ievent.channel, result, None)
    ievent.reply('todo item added')

cmnds.add('todo-chan', handle_chantodo, 'USER')
examples.add('todo-chan', 'todo-chan [<item>] .. add channel todo', \
'todo-chan fix bla')
aliases.data['chantodo'] = 'todo-chan'

def handle_todochandone(bot, ievent):
    """ todo-chandone <listofnrs> .. remove channel todo item """
    if not ievent.rest:
        ievent.missing('<list of nrs>')
        return
    data = ievent.rest.split()
    try:
        nrs = []
        for i in data:
            nrs.append(int(i))
    except ValueError:
        ievent.reply('%s is not an integer' % i)
        return
    nrdone = todo.delete(ievent.channel, nrs)
    if nrdone == 1:
        ievent.reply('%s item deleted' % nrdone)
    elif nrdone == 0:
        ievent.reply('no items deleted')
    else:
        ievent.reply('%s items deleted' % nrdone)

cmnds.add('todo-chandone', handle_todochandone, 'USER')
examples.add('todo-chandone', 'todo-chandone <listofnrs> .. remove item \
from channel todo list', 'todo-chandone 2')
aliases.data['chandone'] = 'todo-chandone'

@locked
def handle_settodo(bot, ievent):
    """ todo-set <name> <txt> .. add to todo of another user """
    try:
        who = ievent.args[0]
        what = ' '.join(ievent.args[1:])
    except IndexError:
        ievent.missing('<nick> <what>')
        return
    if not what:
        ievent.missing('<nick> <what>')
        return
    userhost = getwho(bot, who)
    if not userhost:
        ievent.reply("can't find userhost for %s" % who)
        return
    whouser = users.getuser(userhost)
    if not whouser:
        ievent.reply("can't find user for %s" % userhost)
        return
    user = users.getuser(ievent.userhost)
    if not users.permitted(userhost, user.name, 'todo'):
        ievent.reply("%s doesn't permit todo sharing for %s " % \
(who,user.name))
        return
    what = "%s: %s" % (ievent.nick, what)
    ttime = strtotime(what)
    if not ttime  == None:
        ievent.reply('time detected ' + time.ctime(ttime))
        what = striptime(what)
        todo.add(whouser.name, what, ttime)
    else:
        todo.add(whouser.name, what, None)
    ievent.reply('todo item added')

cmnds.add('todo-set', handle_settodo, 'USER')
examples.add('todo-set', 'todo-set <nick> <txt> .. >set todo item of \
<nick>', 'todo-set dunker bot proggen')

def handle_gettodo(bot, ievent):
    """ todo-get <nick> .. get todo of other user """
    try:
        who = ievent.args[0]
    except IndexError:
        ievent.missing('<nick>')
        return
    userhost = getwho(bot, who)
    if not userhost:
        ievent.reply("can't find userhost for %s" % who)
        return
    whouser = users.getuser(userhost)
    if not whouser:
        ievent.reply("can't find user for %s" % userhost)
        return
    user = users.getuser(ievent.userhost)
    if not users.permitted(userhost, user.name, 'todo'):
        ievent.reply("%s doesn't permit todo sharing for %s " % \
(who,user.name))
        return
    todoos = todo.get(whouser.name)
    saytodo(bot, ievent, todoos)

cmnds.add('todo-get', handle_gettodo, ['USER', 'WEB'])
examples.add('todo-get', 'todo-get <nick> .. get the todo list of \
<nick>', 'todo-get dunker')

def handle_todotime(bot, ievent):
    """ todo-time .. show time related todoos """
    user = users.getuser(ievent.userhost)
    todoos = todo.timetodo(user.name)
    saytodo(bot, ievent, todoos)

cmnds.add('todo-time', handle_todotime, 'USER')
examples.add('todo-time', 'show todo items with time fields', 'todo-time')
aliases.data['tt'] = 'todo-time'

def handle_todoweek(bot, ievent):
    """ todo-week .. show time related todo items for this week """
    user = users.getuser(ievent.userhost)
    todoos = todo.withintime(user.name, today(), today()+7*24*60*60)
    saytodo(bot, ievent, todoos)

cmnds.add('todo-week', handle_todoweek, 'USER')
examples.add('todo-week', 'todo items for this week', 'todo-week')

def handle_today(bot, ievent):
    """ todo-today .. show time related todo items for today """
    user = users.getuser(ievent.userhost)
    todoos = todo.withintime(user.name, today(), today()+24*60*60)
    saytodo(bot, ievent, todoos)

cmnds.add('todo-today', handle_today, 'USER')
examples.add('todo-today', 'todo items for today', 'todo-today')
aliases.data['today'] = 'todo-today'

@locked
def handle_setpriority(bot, ievent):
    """ todo-setprio [<channel|name>] <itemnr> <prio> .. show priority \
        on todo item """
    try:
        (who, itemnr, prio) = ievent.args
    except ValueError:
        try:
            (itemnr, prio) = ievent.args
            user = users.getuser(ievent.userhost)
            who = user.name
        except ValueError:
            ievent.missing('[<channe|namel>] <itemnr> <priority>')
            return
    try:
        itemnr = int(itemnr)
        prio = int(prio)
    except ValueError:
        ievent.missing('[<channel|name>] <itemnr> <priority>')
        return
    who = who.lower()
    todoitems = todo.get(who)
    if not todoitems:
        ievent.reply('no todo items found for %s' % who)
        return
    try:
        todoitems[itemnr].priority = prio
    except KeyError:
        ievent.reply('no todo %s found for %s' % (itemnr, who))
        return
    todo.save()
    ievent.reply('priority set')

cmnds.add('todo-setprio', handle_setpriority, 'USER')
examples.add('todo-setprio', 'todo-setprio [<channel|name>] <itemnr> <prio> \
.. set todo priority', '1) todo-setprio #dunkbots 2 5 2) todo-setprio owner \
3 10 3) todo-setprio 2 10')
aliases.data['setprio'] = 'todo-setprio'

def handle_getpriority(bot, ievent):
    """ todo-getprio <[channel|name]> <itemnr> .. get priority of todo \
        item """
    try:
        (who, itemnr) = ievent.args
    except ValueError:
        try:
            itemnr = ievent.args[0]
            user = users.getuser(ievent.userhost)
            who = user.name
        except IndexError:
            ievent.missing('[<channel|name>] <itemnr>')
            return
    try:
        itemnr = int(itemnr)
    except ValueError:
        ievent.missing('[<channel|name>] <itemnr>')
        return
    who = who.lower()
    todoitems = todo.get(who)
    if not todoitems:
        ievent.reply('no todoitems known for %s' % who)
        return
    try:
        prio = todoitems[itemnr].priority
    except KeyError:
        ievent.reply('no todo item %s known for %s' % (itemnr, who))
        return
    ievent.reply('priority is %s' % prio)

cmnds.add('todo-getprio', handle_getpriority, 'USER')
examples.add('todo-getprio', 'todo-getprio [<channel|name>] <itemnr> .. get \
todo priority', '1) todo-getprio #dunkbots 5 2) todo-getprio 3')
aliases.data['prio'] = 'todo-getprio'

def saytodo(bot, ievent, todoos):
    """ output todo items of <name> """
    result = ""
    now = time.time()
    if not todoos:
        ievent.reply('nothing todo ;]')
        return
    if len(todoos) > 1:
        result += "(%s items) .. " % len(todoos)
    for i in todoos:
        result += "%s) " % i.num
        if i.time:
            if i.time < now:
                result += 'TOO LATE: '
            result += "%s %s " % (time.ctime(i.time), i.descr)
        else:
            result += "%s " % i.descr
        if i.priority:
            result += "[%+d] " % i.priority
    if result:
        ievent.reply(result)
